# ----------------------------------------------------------------------------
# pytform
# Copyright (c) 2008, Michael Henrique Siegwarth
# All rights reserved.
# 
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions 
# are met:
#
#  * Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
#  * Redistributions in binary form must reproduce the above copyright 
#    notice, this list of conditions and the following disclaimer in
#    the documentation and/or other materials provided with the
#    distribution.
#  * Neither the name of pytform nor the names of its
#    contributors may be used to endorse or promote products
#    derived from this software without specific prior written
#    permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ----------------------------------------------------------------------------

from math import sin, cos, acos, radians, sqrt, degrees
import sys
import copy

import pyglet
from pyglet.gl import *
import Box2D2 as box2d

from base.game_object import *

def vector_length(v1, v2):
    return sqrt((v2[0]-v1[0])**2 + (v2[1]-v1[1])**2)

def get_angle(va, vb, vc):
    lb = vector_length(va, vb)
    lc = vector_length(va, vc)
    la = vector_length(vb, vc)

    return degrees(acos((lb**2 + lc**2 - la**2) / (2 * lb * lc)))

def find_center_point(vertex_array):
    # Get the left most x
    left_x = sys.maxint
    for v in vertex_array:
        if left_x > v[0]:
            left_x = v[0]
            
    # Get the top most y
    top_y = sys.maxint
    for v in vertex_array:
        if top_y > v[1]:
            top_y = v[1]
        
    # Get the right most x
    right_x = 0
    for v in vertex_array:
        if right_x < v[0]:
            right_x = v[0]
    
    # Get the bottom most y
    bottom_y = 0
    for v in vertex_array:
        if bottom_y < v[1]:
            bottom_y = v[1]
    
    return ((left_x+right_x)/2, (top_y+bottom_y)/2)

class Triangle(GameObject):
    def __init__(self, x, y, angle, world):
        super(Triangle, self).__init__(x, y, angle, False, world)

        self.mode = pyglet.gl.GL_TRIANGLES
        
    def shapify(self, body, vertices):
        # It is supposed that every Triangle subclass has a vertex list
        shape_def = box2d.b2PolygonDef()
        shape_def.vertexCount = 3
        shape_def.density = 1
        shape_def.friction = 0.1
        
        # Get smallest X
        x = sys.maxint
        smallest_x = None
        smallest_index = -1
        i = 0
        for v in vertices:
            if v[0] < x:
                x = v[0]
                smallest_x = v
                smallest_index = i
                
            i += 1
        
        # Get largest X
        x = 0
        largest_x = None
        largest_index = -1
        i = 0
        for v in vertices:
            if v[0] > x:
                x = v[0]
                largest_x = v
                largest_index = i
            i += 1

        remaining_index = (smallest_index + largest_index - 3) * -1

        if vertices[remaining_index][1] > vertices[smallest_index][1]:
            shape_def.setVertex(0, vertices[smallest_index][0], 
                                   vertices[smallest_index][1])
            shape_def.setVertex(1, vertices[largest_index][0], 
                                   vertices[largest_index][1])
            shape_def.setVertex(2, vertices[remaining_index][0], 
                                   vertices[remaining_index][1])
        else:
            shape_def.setVertex(0, vertices[smallest_index][0], 
                                   vertices[smallest_index][1])
            shape_def.setVertex(1, vertices[remaining_index][0], 
                                   vertices[remaining_index][1])
            shape_def.setVertex(2, vertices[largest_index][0], 
                                   vertices[largest_index][1])

        try:
            shape_def.checkValues()
            body.CreateShape(shape_def).asPolygon()
        except ValueError:
            print "Too small triangle found"


class NiceTriangle(Triangle):
    def __init__(self, x, y, angle, world, batch):
        super(NiceTriangle, self).__init__(x, y, angle, world)

        # Create vertex list
        self.vertex_list.append(batch.add(3, self.mode, None,
            'v2f',
            ('c3f/static', (0, 1, 0, 1, 1, 0, 1, 0, 1))
        ))

        if world != None:
            # Not a terrain triangle, so create a body for it
            # Terrain triangles all go to the same terrain body
            v = [(-0.4, -0.4), (0.4, -0.4), (0, 0.4)]
            self.shapify(self.body, v)
            self.body.SetMassFromShapes()
            self.update_vertices()

        self.events.append(["player_object.PlayerObject", self.scream])

    def scream(self, trigger_object, colliding_shape):
        print "NiceTriangle says: the player has hit me!"

class TerrainTriangle(Triangle):
    def __init__(self, v, tv, c, texture, body, batch):
        super(TerrainTriangle, self).__init__(0, 0, 0, None)

        self.texture = texture

        # Create vertex list
        if texture != None:
            self.group = pyglet.graphics.TextureGroup(texture.get_texture())
            self.vertex_list.append(batch.add(3, self.mode, self.group,
                ('v2f/static', (v[0][0], v[0][1], v[1][0], v[1][1], v[2][0], v[2][1])),
                ('c3f/static', (c[0][0], c[0][1], c[0][2], c[1][0], c[1][1], c[1][2], c[2][0], c[2][1], c[2][2])),
                ('t2f/static', (tv[0][0], tv[0][1], tv[1][0], tv[1][1], tv[2][0], tv[2][1]))
            ))
        else:
            self.vertex_list.append(batch.add(3, self.mode, None,
                ('v2f/static', (v[0][0], v[0][1], v[1][0], v[1][1], v[2][0], v[2][1])),
                ('c3f/static', (c[0][0], c[0][1], c[0][2], c[1][0], c[1][1], c[1][2], c[2][0], c[2][1], c[2][2])),
                ('t2f/static', (tv[0][0], tv[0][1], tv[1][0], tv[1][1], tv[2][0], tv[2][1]))
            ))

        if body != None:
            self.shapify(body, v)

    def like_array(self):
        arr = []
        arr_item = []
        for i in range(3):
            arr_item.append(str(self.vertices[i][0]))
            arr_item.append(str(self.vertices[i][1]))
            arr_item.append(str(self.texture_vertices[i][0]))
            arr_item.append(str(self.texture_vertices[i][1]))
            arr_item.append(str(self.color_vertices[i][0]))
            arr_item.append(str(self.color_vertices[i][1]))
            arr_item.append(str(self.color_vertices[i][2]))
            arr.append(arr_item)
            arr_item = []
        
        return arr

    def move_vertex(self, vertex, (x,y)):
        ve = self.vertices

        ve[vertex][0] += x
        ve[vertex][1] += y

        self.vertices = ve
    
    def move(self, (x, y)):
        ve = self.vertices

        for v in ve:
            v[0] += x
            v[1] += y

        self.vertices = ve
    
    def move_texture_vertex(self, vertex, (x, y)):
        tve = self.texture_vertices
        
        tve[vertex][0] += x
        tve[vertex][1] += y

        self.texture_vertices = tve
        
    def move_texture_vertices(self, (x, y)):
        tve = self.texture_vertices
        
        for v in tve:
            v[0] += x
            v[1] += y

        self.texture_vertices = tve

    def rotate(self, angle):
        (cx, cy) = find_center_point(self.vertices)

        ve = self.vertices
        
        for v in ve:
            x_temp = cos(radians(angle))*(v[0] - cx) - sin(radians(angle))*(v[1] - cy)
            y_temp = sin(radians(angle))*(v[0] - cx) + cos(radians(angle))*(v[1] - cy)
            v[0] = x_temp + cx
            v[1] = y_temp + cy

        self.vertices = ve

    def rotate_texture(self, angle):    
        (cx, cy) = find_center_point(self.texture_vertices)

        tve = self.texture_vertices
        
        for v in tve:
            x_temp = cos(radians(angle))*(v[0] - cx) - sin(radians(angle))*(v[1] - cy)
            y_temp = sin(radians(angle))*(v[0] - cx) + cos(radians(angle))*(v[1] - cy)
            v[0] = x_temp + cx
            v[1] = y_temp + cy

        self.texture_vertices = tve
    
    def scale(self, factor):        
        (cx, cy) = find_center_point(self.vertices)

        ve = self.vertices
        
        for v in ve:
            v[0] = factor*(v[0] - cx) + cx
            v[1] = factor*(v[1] - cy) + cy

        self.vertices = ve

    def scale_texture(self, factor):     
        (cx, cy) = find_center_point(self.texture_vertices)

        tve = self.texture_vertices
        
        for v in tve:
            v[0] = factor*(v[0] - cx) + cx
            v[1] = factor*(v[1] - cy) + cy

        self.texture_vertices = tve

    def paint_vertex(self, vertex, color, value):
        cve = self.color_vertices
        
        cve[vertex][color] += value/200.0

        if cve[vertex][color] > 1.0:
            cve[vertex][color] = 0.0
        elif cve[vertex][color] < 0.0:
            cve[vertex][color] = 1.0
            
        self.color_vertices = cve

    def paint(self, color, value):
        cve = self.color_vertices

        for cv in cve:
            cv[color] += value/200.0

            if cv[color] > 1.0:
                cv[color] = 0.0
            elif cv[color] < 0.0:
                cv[color] = 1.0

        self.color_vertices = cve
            
    def get_tv(self):
        v = list(self.vertex_list[0].tex_coords)
        vertices2 = []
        vertices2.append([v[0], v[1]])
        vertices2.append([v[2], v[3]])
        vertices2.append([v[4], v[5]])

        return vertices2
    
    def set_tv(self, v):
        new_v = v[0] + v[1] + v[2]
        self.vertex_list[0].tex_coords = new_v
        
    texture_vertices = property(get_tv, set_tv)

    def get_v(self):
        v = list(self.vertex_list[0].vertices)
        vertices2 = []
        vertices2.append([v[0], v[1]])
        vertices2.append([v[2], v[3]])
        vertices2.append([v[4], v[5]])

        return vertices2
    
    def set_v(self, v):
        new_v = v[0] + v[1] + v[2]
        self.vertex_list[0].vertices = new_v
    
    vertices = property(get_v, set_v)

    def get_color(self):
        v = list(self.vertex_list[0].colors)
        vertices2 = []
        vertices2.append([v[0], v[1], v[2]])
        vertices2.append([v[3], v[4], v[5]])
        vertices2.append([v[6], v[7], v[8]])

        return vertices2

    def set_color(self, v):
        new_v = v[0] + v[1] + v[2]
        self.vertex_list[0].colors = new_v

    color_vertices = property(get_color, set_color)

    def set_texture(self, texture, batch):
        self.texture = texture
        
        v = self.vertex_list[0]

        if texture != None:
            self.group = pyglet.graphics.TextureGroup(texture.get_texture())
        else:
            self.group = None

        self.vertex_list[0] = batch.add(3, self.mode, self.group,
            'v2f', 'c3f', 't2f')

        self.vertex_list[0].vertices = list(v.vertices)
        self.vertex_list[0].tex_coords = list(v.tex_coords)
        self.vertex_list[0].colors = list(v.colors)
        
        v.delete()

    def contains(self, coord):
        v = self.vertices
        angle = get_angle(coord, v[0], v[1]) 
        angle += get_angle(coord, v[1], v[2])
        angle += get_angle(coord, v[0], v[2])

        # using 360 caused a bug when clicking on the edge of the triangle
        if angle >= 358:
            return True
        else:
            return False
